home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 7 / Amiga Format AFCD07 (Dec 1996, Issue 91).iso / serious / shareware / comms / non-internet / samba / source / smbpasswd.c < prev    next >
C/C++ Source or Header  |  1996-06-26  |  12KB  |  457 lines

  1. #ifdef SMB_PASSWD
  2.  
  3. /*
  4.  * Unix SMB/Netbios implementation. Version 1.9. smbpasswd module. Copyright
  5.  * (C) Jeremy Allison 1995.
  6.  * 
  7.  * This program is free software; you can redistribute it and/or modify it under
  8.  * the terms of the GNU General Public License as published by the Free
  9.  * Software Foundation; either version 2 of the License, or (at your option)
  10.  * any later version.
  11.  * 
  12.  * This program is distributed in the hope that it will be useful, but WITHOUT
  13.  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14.  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  15.  * more details.
  16.  * 
  17.  * You should have received a copy of the GNU General Public License along with
  18.  * this program; if not, write to the Free Software Foundation, Inc., 675
  19.  * Mass Ave, Cambridge, MA 02139, USA.
  20.  */
  21.  
  22. #include "includes.h"
  23. #include "des.h"
  24.  
  25. /* Static buffers we will return. */
  26. static struct smb_passwd pw_buf;
  27. static pstring  user_name;
  28. static unsigned char smbpwd[16];
  29. static unsigned char smbntpwd[16];
  30.  
  31. static int gethexpwd(char *p, char *pwd)
  32. {
  33.     int i;
  34.     unsigned char   lonybble, hinybble;
  35.     char           *hexchars = "0123456789ABCDEF";
  36.     char           *p1, *p2;
  37.     for (i = 0; i < 32; i += 2) {
  38.         hinybble = toupper(p[i]);
  39.         lonybble = toupper(p[i + 1]);
  40.  
  41.         p1 = strchr(hexchars, hinybble);
  42.         p2 = strchr(hexchars, lonybble);
  43.         if (!p1 || !p2)
  44.             return (False);
  45.  
  46.         hinybble = PTR_DIFF(p1, hexchars);
  47.         lonybble = PTR_DIFF(p2, hexchars);
  48.  
  49.         pwd[i / 2] = (hinybble << 4) | lonybble;
  50.     }
  51.     return (True);
  52. }
  53.  
  54. struct smb_passwd *
  55. _my_get_smbpwnam(FILE * fp, char *name, BOOL * valid_old_pwd, 
  56.         BOOL *got_valid_nt_entry, long *pwd_seekpos)
  57. {
  58.     char            linebuf[256];
  59.     unsigned char   c;
  60.     unsigned char  *p;
  61.     long            uidval;
  62.     long            linebuf_len;
  63.  
  64.     /*
  65.      * Scan the file, a line at a time and check if the name matches.
  66.      */
  67.     while (!feof(fp)) {
  68.         linebuf[0] = '\0';
  69.         *pwd_seekpos = ftell(fp);
  70.  
  71.         fgets(linebuf, 256, fp);
  72.         if (ferror(fp))
  73.             return NULL;
  74.  
  75.         /*
  76.          * Check if the string is terminated with a newline - if not
  77.          * then we must keep reading and discard until we get one.
  78.          */
  79.         linebuf_len = strlen(linebuf);
  80.         if (linebuf[linebuf_len - 1] != '\n') {
  81.             c = '\0';
  82.             while (!ferror(fp) && !feof(fp)) {
  83.                 c = fgetc(fp);
  84.                 if (c == '\n')
  85.                     break;
  86.             }
  87.         } else
  88.             linebuf[linebuf_len - 1] = '\0';
  89.  
  90.         if ((linebuf[0] == 0) && feof(fp))
  91.             break;
  92.         /*
  93.          * The line we have should be of the form :-
  94.          * 
  95.          * username:uid:[32hex bytes]:....other flags presently
  96.          * ignored....
  97.          * 
  98.          * or,
  99.          * 
  100.          * username:uid:[32hex bytes]:[32hex bytes]:....ignored....
  101.          * 
  102.          * if Windows NT compatible passwords are also present.
  103.          */
  104.  
  105.         if (linebuf[0] == '#' || linebuf[0] == '\0')
  106.             continue;
  107.         p = (unsigned char *) strchr(linebuf, ':');
  108.         if (p == NULL)
  109.             continue;
  110.         /*
  111.          * As 256 is shorter than a pstring we don't need to check
  112.          * length here - if this ever changes....
  113.          */
  114.         strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
  115.         user_name[PTR_DIFF(p, linebuf)] = '\0';
  116.         if (!strequal(user_name, name))
  117.             continue;
  118.  
  119.         /* User name matches - get uid and password */
  120.         p++;        /* Go past ':' */
  121.         if (!isdigit(*p))
  122.             return (False);
  123.  
  124.         uidval = atoi((char *) p);
  125.         while (*p && isdigit(*p))
  126.             p++;
  127.  
  128.         if (*p != ':')
  129.             return (False);
  130.  
  131.         /*
  132.          * Now get the password value - this should be 32 hex digits
  133.          * which are the ascii representations of a 16 byte string.
  134.          * Get two at a time and put them into the password.
  135.          */
  136.         p++;
  137.         *pwd_seekpos += PTR_DIFF(p, linebuf);    /* Save exact position
  138.                              * of passwd in file -
  139.                              * this is used by
  140.                              * smbpasswd.c */
  141.         if (*p == '*' || *p == 'X') {
  142.             /* Password deliberately invalid - end here. */
  143.             *valid_old_pwd = False;
  144.             *got_valid_nt_entry = False;
  145.             pw_buf.smb_nt_passwd = NULL;    /* No NT password (yet)*/
  146.  
  147.             /* Now check if the NT compatible password is
  148.                available. */
  149.             p += 33; /* Move to the first character of the line after 
  150.                         the lanman password. */
  151.             if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
  152.                 /* NT Entry was valid - even if 'X' or '*', can be overwritten */
  153.                 *got_valid_nt_entry = True;
  154.                 if (*p != '*' && *p != 'X') {
  155.                     if(gethexpwd(p,smbntpwd))
  156.                         pw_buf.smb_nt_passwd = smbntpwd;
  157.                 }
  158.             }
  159.             pw_buf.smb_name = user_name;
  160.             pw_buf.smb_userid = uidval;
  161.             pw_buf.smb_passwd = NULL;    /* No password */
  162.             return (&pw_buf);
  163.         }
  164.         if (linebuf_len < (PTR_DIFF(p, linebuf) + 33))
  165.             return (False);
  166.  
  167.         if (p[32] != ':')
  168.             return (False);
  169.  
  170.         if (!strncasecmp(p, "NO PASSWORD", 11)) {
  171.             pw_buf.smb_passwd = NULL;    /* No password */
  172.         } else {
  173.             if(!gethexpwd(p,smbpwd))
  174.                 return False;
  175.             pw_buf.smb_passwd = smbpwd;
  176.         }
  177.  
  178.         pw_buf.smb_name = user_name;
  179.         pw_buf.smb_userid = uidval;
  180.         pw_buf.smb_nt_passwd = NULL;
  181.         *got_valid_nt_entry = False;
  182.         *valid_old_pwd = True;
  183.  
  184.         /* Now check if the NT compatible password is
  185.            available. */
  186.         p += 33; /* Move to the first character of the line after 
  187.                     the lanman password. */
  188.         if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
  189.             /* NT Entry was valid - even if 'X' or '*', can be overwritten */
  190.             *got_valid_nt_entry = True;
  191.             if (*p != '*' && *p != 'X') {
  192.                 if(gethexpwd(p,smbntpwd))
  193.                     pw_buf.smb_nt_passwd = smbntpwd;
  194.             }
  195.         }
  196.         return &pw_buf;
  197.     }
  198.     return NULL;
  199. }
  200.  
  201. /*
  202.  * Print command usage on stderr and die.
  203.  */
  204. void 
  205. usage(char *name)
  206. {
  207.     fprintf(stderr, "Usage is : %s [username]\n", name);
  208.     exit(1);
  209. }
  210.  
  211. int main(int argc, char **argv)
  212. {
  213.   int             real_uid;
  214.   struct passwd  *pwd;
  215.   fstring         old_passwd;
  216.   uchar           old_p16[16];
  217.   uchar           old_nt_p16[16];
  218.   fstring         new_passwd;
  219.   uchar           new_p16[16];
  220.   uchar           new_nt_p16[16];
  221.   char           *p;
  222.   struct smb_passwd *smb_pwent;
  223.   FILE           *fp;
  224.   BOOL            valid_old_pwd = False;
  225.   BOOL             got_valid_nt_entry = False;
  226.   long            seekpos;
  227.   int             pwfd;
  228.   char            ascii_p16[66];
  229.   char            c;
  230.   int             ret, i, err, writelen;
  231.   int             lockfd = -1;
  232.   char           *pfile = SMB_PASSWD_FILE;
  233.   char            readbuf[16 * 1024];
  234.   
  235.   setup_logging(argv[0],True);
  236.   
  237.   charset_initialise();
  238.   
  239. #ifndef DEBUG_PASSWORD
  240.   /* Check the effective uid */
  241.   if (geteuid() != 0) {
  242.     fprintf(stderr, "%s: Must be setuid root.\n", argv[0]);
  243.     exit(1);
  244.   }
  245. #endif
  246.   
  247.   /* Get the real uid */
  248.   real_uid = getuid();
  249.   
  250.   /* Deal with usage problems */
  251.   if (real_uid == 0) {
  252.     /* As root we can change anothers password. */
  253.     if (argc != 1 && argc != 2)
  254.       usage(argv[0]);
  255.   } else if (argc != 1)
  256.     usage(argv[0]);
  257.   
  258.   
  259.   if (real_uid == 0 && argc == 2) {
  260.     /* If we are root we can change anothers password. */
  261.     strncpy(user_name, argv[1], sizeof(user_name) - 1);
  262.     user_name[sizeof(user_name) - 1] = '\0';
  263.     pwd = getpwnam(user_name);
  264.   } else {
  265.     pwd = getpwuid(real_uid);
  266.   }
  267.   
  268.   if (pwd == 0) {
  269.     fprintf(stderr, "%s: Unable to get UNIX password entry for user.\n", argv[0]);
  270.     exit(1);
  271.   }
  272.   /* If we are root we don't ask for the old password. */
  273.   old_passwd[0] = '\0';
  274.   if (real_uid != 0) {
  275.     p = getpass("Old SMB password:");
  276.     strncpy(old_passwd, p, sizeof(fstring));
  277.     old_passwd[sizeof(fstring)-1] = '\0';
  278.   }
  279.   new_passwd[0] = '\0';
  280.   p = getpass("New SMB password:");
  281.   strncpy(new_passwd, p, sizeof(fstring));
  282.   new_passwd[sizeof(fstring)-1] = '\0';
  283.   p = getpass("Retype new SMB password:");
  284.   if (strcmp(p, new_passwd)) {
  285.     fprintf(stderr, "%s: Mismatch - password unchanged.\n", argv[0]);
  286.     exit(1);
  287.   }
  288.   
  289.   if (new_passwd[0] == '\0') {
  290.     printf("Password not set\n");
  291.     exit(0);
  292.   }
  293.   
  294.   /* Calculate the MD4 hash (NT compatible) of the old and new passwords */
  295.   memset(old_nt_p16, '\0', 16);
  296.   E_md4hash((uchar *)old_passwd, old_nt_p16);
  297.   
  298.   memset(new_nt_p16, '\0', 16);
  299.   E_md4hash((uchar *) new_passwd, new_nt_p16);
  300.   
  301.   /* Mangle the passwords into Lanman format */
  302.   old_passwd[14] = '\0';
  303.   strupper(old_passwd);
  304.   new_passwd[14] = '\0';
  305.   strupper(new_passwd);
  306.   
  307.   /*
  308.    * Calculate the SMB (lanman) hash functions of both old and new passwords.
  309.    */
  310.   
  311.   memset(old_p16, '\0', 16);
  312.   E_P16((uchar *) old_passwd, old_p16);
  313.   
  314.   memset(new_p16, '\0', 16);
  315.   E_P16((uchar *) new_passwd, new_p16);
  316.   
  317.   /*
  318.    * Open the smbpaswd file XXXX - we need to parse smb.conf to get the
  319.    * filename
  320.    */
  321.   if ((fp = fopen(pfile, "r+")) == NULL) {
  322.     err = errno;
  323.     fprintf(stderr, "%s: Failed to open password file %s.\n",
  324.         argv[0], pfile);
  325.     errno = err;
  326.     perror(argv[0]);
  327.     exit(err);
  328.   }
  329.   /* Set read buffer to 16k for effiecient reads */
  330.   setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
  331.   
  332.   /* make sure it is only rw by the owner */
  333.   chmod(pfile, 0600);
  334.   
  335.   /* Lock the smbpasswd file for write. */
  336.   if ((lockfd = pw_file_lock(pfile, F_WRLCK, 5)) < 0) {
  337.     err = errno;
  338.     fprintf(stderr, "%s: Failed to lock password file %s.\n",
  339.         argv[0], pfile);
  340.     fclose(fp);
  341.     errno = err;
  342.     perror(argv[0]);
  343.     exit(err);
  344.   }
  345.   /* Get the smb passwd entry for this user */
  346.   smb_pwent = _my_get_smbpwnam(fp, pwd->pw_name, &valid_old_pwd, 
  347.                    &got_valid_nt_entry, &seekpos);
  348.   if (smb_pwent == NULL) {
  349.     fprintf(stderr, "%s: Failed to find entry for user %s in file %s.\n",
  350.         argv[0], pwd->pw_name, pfile);
  351.     fclose(fp);
  352.     pw_file_unlock(lockfd);
  353.     exit(1);
  354.   }
  355.   /* If we are root we don't need to check the old password. */
  356.   if (real_uid != 0) {
  357.     if ((valid_old_pwd == False) || (smb_pwent->smb_passwd == NULL)) {
  358.       fprintf(stderr, "%s: User %s is disabled, plase contact your administrator to enable it.\n", argv[0], pwd->pw_name);
  359.       fclose(fp);
  360.       pw_file_unlock(lockfd);
  361.       exit(1);
  362.     }
  363.     /* Check the old Lanman password */
  364.     if (memcmp(old_p16, smb_pwent->smb_passwd, 16)) {
  365.       fprintf(stderr, "%s: Couldn't change password.\n", argv[0]);
  366.       fclose(fp);
  367.       pw_file_unlock(lockfd);
  368.       exit(1);
  369.     }
  370.     /* Check the NT password if it exists */
  371.     if (smb_pwent->smb_nt_passwd != NULL) {
  372.       if (memcmp(old_nt_p16, smb_pwent->smb_nt_passwd, 16)) {
  373.     fprintf(stderr, "%s: Couldn't change password.\n", argv[0]);
  374.     fclose(fp);
  375.     pw_file_unlock(lockfd);
  376.     exit(1);
  377.       }
  378.     }
  379.   }
  380.   /*
  381.    * If we get here either we were root or the old password checked out
  382.    * ok.
  383.    */
  384.   /* Create the 32 byte representation of the new p16 */
  385.   for (i = 0; i < 16; i++) {
  386.     sprintf(&ascii_p16[i * 2], "%02X", (uchar) new_p16[i]);
  387.   }
  388.   if(got_valid_nt_entry) {
  389.     /* Add on the NT md4 hash */
  390.     ascii_p16[32] = ':';
  391.     for (i = 0; i < 16; i++) {
  392.       sprintf(&ascii_p16[(i * 2)+33], "%02X", (uchar) new_nt_p16[i]);
  393.     }
  394.   }
  395.   /*
  396.    * Do an atomic write into the file at the position defined by
  397.    * seekpos.
  398.    */
  399.   pwfd = fileno(fp);
  400.   ret = lseek(pwfd, seekpos - 1, SEEK_SET);
  401.   if (ret != seekpos - 1) {
  402.     err = errno;
  403.     fprintf(stderr, "%s: seek fail on file %s.\n",
  404.         argv[0], pfile);
  405.     fclose(fp);
  406.     errno = err;
  407.     perror(argv[0]);
  408.     pw_file_unlock(lockfd);
  409.     exit(1);
  410.   }
  411.   /* Sanity check - ensure the character is a ':' */
  412.   if (read(pwfd, &c, 1) != 1) {
  413.     err = errno;
  414.     fprintf(stderr, "%s: read fail on file %s.\n",
  415.         argv[0], pfile);
  416.     fclose(fp);
  417.     errno = err;
  418.     perror(argv[0]);
  419.     pw_file_unlock(lockfd);
  420.     exit(1);
  421.   }
  422.   if (c != ':') {
  423.     fprintf(stderr, "%s: sanity check on passwd file %s failed.\n",
  424.         argv[0], pfile);
  425.     fclose(fp);
  426.     pw_file_unlock(lockfd);
  427.     exit(1);
  428.   }
  429.   writelen = (got_valid_nt_entry) ? 65 : 32;
  430.   if (write(pwfd, ascii_p16, writelen) != writelen) {
  431.     err = errno;
  432.     fprintf(stderr, "%s: write fail in file %s.\n",
  433.         argv[0], pfile);
  434.     fclose(fp);
  435.     errno = err;
  436.     perror(argv[0]);
  437.     pw_file_unlock(lockfd);
  438.     exit(err);
  439.   }
  440.   fclose(fp);
  441.   pw_file_unlock(lockfd);
  442.   printf("Password changed\n");
  443.   return 0;
  444. }
  445.  
  446. #else
  447.  
  448. #include "includes.h"
  449.  
  450. int 
  451. main(int argc, char **argv)
  452. {
  453.   printf("smb password encryption not selected in Makefile\n");
  454.   return 0;
  455. }
  456. #endif
  457.